package com.google.code.gaeapptest.util;

import static bufferings.ktr.wjr.shared.util.Preconditions.checkNotNull;

import java.io.File;
import java.io.FileFilter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.List;

import bufferings.ktr.wjr.server.util.ClassNotFoundRuntimeException;

import com.google.code.gaeapptest.domain.Test;
import com.google.code.gaeapptest.domain.TestCategory;

public class ClassScanUtil {
	private static File[] listDirectories(File dir) {
		return dir.listFiles(new FileFilter() {
			public boolean accept(File pathname) {
				return pathname.isDirectory();
			}
		});
	}

	private static File[] listClassFiles(File dir) {
		return dir.listFiles(new FileFilter() {
			public boolean accept(File pathname) {
				return !pathname.isDirectory()
						&& pathname.getName().endsWith(".class");
			}
		});
	}

	private static String getClassNameFromClassFile(String searchRootDirPath,
			File classFile) {
		String path = classFile.getPath();

		int beginIndex = searchRootDirPath.length() + 1;
		int endIndex = path.length() - ".class".length();
		return path.substring(beginIndex, endIndex).replaceAll("[\\\\/]", ".");
	}

	public static Class<?> loadClass(String className) {
		checkNotNull(className, "The className parameter is null.");
		try {
			return Thread.currentThread().getContextClassLoader()
					.loadClass(className);
		} catch (ClassNotFoundException e1) {
			throw new ClassNotFoundRuntimeException("Cannot load class("
					+ className + ").", e1);
		}
	}

	public static void findAndStoreTests(List<TestCategory> categories,
			String searchRootDirPath, File targetDir) {
		File[] classFiles = listClassFiles(targetDir);
		for (File classFile : classFiles) {
			String className = getClassNameFromClassFile(searchRootDirPath,
					classFile);
			if (className.endsWith("Tests")) {
				Class<?> clazz = loadClass(className);
				Method dm[] = clazz.getDeclaredMethods();
				if (dm != null && dm.length > 0) {
					TestCategory tc = new TestCategory();
					categories.add(tc);
					tc.category = className.substring(
							className.lastIndexOf(".") + 1,
							className.lastIndexOf("Tests"));
					for (Method m : dm) {
						Annotation[] as = m.getAnnotations();
						if (as != null
								&& as.length > 0
								&& as[0].annotationType().equals(
										org.junit.Test.class)) {
							Test t = new Test(m.getName());
							tc.tests.add(t);
							System.out.println("category:" + tc.category
									+ " test:" + m.getName());
						}
					}
				}
			}
		}

		File[] dirs = listDirectories(targetDir);
		for (File dir : dirs) {
			findAndStoreTests(categories, searchRootDirPath, dir);
		}
	}

}
